﻿using System.Collections.ObjectModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace HMEngine.HMInputs {
    public enum HMMouseButton {
        LeftButton = 0,
        MiddleButton = 1,
        RightButton = 2,
        XButton1 = 3,
        XButton2 = 4
    }

    public delegate void HMMouseClickedHandler(Point pos, Collection<HMMouseButton> buttons);
    public delegate void HMMouseHeldHandler(Point pos, Collection<HMMouseButton> buttons);
    public delegate void HMMouseReleasedHandler(Point pos, Collection<HMMouseButton> buttons);
    public delegate void HMMouseMovedHandler(Vector2 move);
    public delegate void HMMouseScrolledHandler(int ticks);

    public sealed class HMMouseDevice : HMInputDevice {
        private MouseState mouseState;
        private Point initialMousePosition;
        private int lastScrollPosition;
        private bool freezeMouse;

        private Collection<HMPressedState> buttonStates;
        private Collection<ButtonState> pressedStates;

        private Collection<HMMouseButton> pressed;
        private Collection<HMMouseButton> held;
        private Collection<HMMouseButton> released;

        public event HMMouseClickedHandler OnMouseClicked;
        public event HMMouseHeldHandler OnMouseHeld;
        public event HMMouseReleasedHandler OnMouseReleased;
        public event HMMouseMovedHandler OnMouseMoved;
        public event HMMouseScrolledHandler OnMouseScrolled;

        public bool Freeze {
            get { return freezeMouse; }
            set {
                freezeMouse = value;
                if (freezeMouse) {
                    mouseState = Mouse.GetState();
                }
                initialMousePosition = new Point(mouseState.X, mouseState.Y);
            }
        }

        public override void Initialize() {
            mouseState = Mouse.GetState();
            initialMousePosition = new Point(mouseState.X, mouseState.Y);
            lastScrollPosition = mouseState.ScrollWheelValue;

            buttonStates = new Collection<HMPressedState>();
            for (int i = 0; i < 5; i++) {
                buttonStates.Add(HMPressedState.Idle);
            }
        }

        public override void Update(GameTime gameTime) {
            // Mouse Wheel Checks
            int scrollWheelValue = mouseState.ScrollWheelValue;
            int scrollMoved = lastScrollPosition - scrollWheelValue;
            lastScrollPosition = scrollWheelValue;

            // Mouse Move Checks
            mouseState = Mouse.GetState();
            var currentMousePosition = new Point(mouseState.X, mouseState.Y);
            var mouseMoved = new Vector2(
                currentMousePosition.X - initialMousePosition.X,
                currentMousePosition.Y - initialMousePosition.Y
                );

            if (freezeMouse) {
                Mouse.SetPosition(initialMousePosition.X, initialMousePosition.Y);
            } else {
                initialMousePosition = currentMousePosition;
            }

            // Mouse Buttons Checks
            pressed = new Collection<HMMouseButton>();
            held = new Collection<HMMouseButton>();
            released = new Collection<HMMouseButton>();
            pressedStates = MousePressedStateArray(mouseState);
            for (int i = 0; i < 5; i++) {
                buttonStates[i] = CheckPressedState(pressedStates[i], buttonStates[i]);
            }

            // Loading Event Lists
            for (int i = 0; i < 5; i++) {
                if (buttonStates[i] == HMPressedState.Pressed) {
                    pressed.Add((HMMouseButton)i);
                } else if (buttonStates[i] == HMPressedState.Held) {
                    held.Add((HMMouseButton)i);
                } else if (buttonStates[i] == HMPressedState.Released) {
                    released.Add((HMMouseButton)i);
                }
            }

            // Event Calls
            if (scrollMoved != 0 && null != OnMouseScrolled) {
                OnMouseScrolled(scrollMoved);
            }
            if (mouseMoved.Length() > 0 && null != OnMouseMoved) {
                OnMouseMoved(mouseMoved);
            }
            if (pressed.Count > 0 && null != OnMouseClicked) {
                OnMouseClicked(currentMousePosition, pressed);
            }
            if (held.Count > 0 && null != OnMouseHeld) {
                OnMouseHeld(currentMousePosition, held);
            }
            if (released.Count > 0 && null != OnMouseReleased) {
                OnMouseReleased(currentMousePosition, released);
            }
        }

        private static Collection<ButtonState> MousePressedStateArray(MouseState mouse) {
            var states = new Collection<ButtonState> {
                mouse.LeftButton,
                mouse.MiddleButton,
                mouse.RightButton,
                mouse.XButton1,
                mouse.XButton2
            };

            return states;
        }

        public override void Dispose() { }
    }
}